#include <iomanip>
#include <iostream>

#include "../headers/ExecutionContext.h"
#include "../headers/CPU.h"
#include "../headers/CpuHalted.h"

using namespace std;

ExecutionContext::ExecutionContext(void)
{
}


ExecutionContext::ExecutionContext(ExecutionContext &context)
{
	// deep copy the breakpoints
	for(set<unsigned int>::const_iterator it = context.breakpoints.begin(); it != context.breakpoints.end(); it++)
	{
		this->breakpoints.insert(*it);
	}
}


ExecutionContext::~ExecutionContext(void)
{
	this->breakpoints.clear();
}


set<unsigned int>* ExecutionContext::get_breakpoints()
{
	return &(this->breakpoints);
}


void ExecutionContext::run(CPU* const target)
{
	bool proceed = true;
	bool isHalt = false;
	cout << "Running..." << endl;
	while(proceed)
	{
		try
		{
			target->execute(target->instruction_word());

			// did we hit a breakpoint?
			proceed &= (this->breakpoints.find(target->instruction_pointer()) == this->breakpoints.end());
		}
		catch(CpuHalted)
		{
			proceed = false;
			isHalt = true;

			#if _DEBUG
			const int HaltOpCode = 5;
			const int HaltFieldSpec = 2;

			_ASSERT_EXPR(target->instruction_word()->OpCode() == HaltOpCode && target->InstructionWord()->modification_code() == HaltFieldSpec
				, "A HLT was thrown but the instruction is not a HLT instruction");
			#endif
		}
	}

	if(isHalt)
	{
		cout << "CPU Halted" << endl;
	}
	else
	{
		cout << "Breakpoint hit @ 0x" << std::hex << std::setfill('0') << std::setw(ADDRESS_WIDTH) << target->instruction_pointer();
		#ifdef OUTPUTINSTRUCTIONS
		cout << " - next instruction:";
		target->write_instruction(target->instruction_word());
		#endif
		cout << endl;
	}
}


void ExecutionContext::step(CPU* const target)
{
	try
	{
		target->execute(target->instruction_word());
	}
	catch(CpuHalted)
	{
		cout << "CPU Halted" << endl;
	}
}
